import { AxiosRequestConfig, AxiosResponse, InterceptorManager, Interceptor } from "./types";
import qs from 'qs';
import parseHeaders from 'parse-headers';

let defaults: AxiosRequestConfig = {
    method: 'get',
    timeout: 10000,
    headers: {
        common: {
            accept: 'application/json'
        }
    },
    transformRequest: (data: any, headers: any) => {
        headers.common['content-type'] = 'application/json'
        return JSON.stringify(data);
    },
    transformResponse: (data: any) => {
        return data.data
    }
}
let getStyleMethods = ['get', 'head', 'delete', 'options'];
let postStyleMethods = ['put', 'post'];
getStyleMethods.forEach((method: string) => {
    defaults.headers![method] = {}
});
postStyleMethods.forEach((method: string) => {
    defaults.headers![method] = {
        'content-type': 'application/json'
    }
})
let allMethods = [...getStyleMethods, ...postStyleMethods];

export default class Axios<T> {
    public defaults: AxiosRequestConfig = defaults
    public interceptors = {
        request: new InterceptorManager<AxiosRequestConfig>,
        response: new InterceptorManager<AxiosResponse<T>>
    }
    // t用来限制response里的data类型
    request(config: AxiosRequestConfig): Promise<AxiosRequestConfig | AxiosResponse<T>> {
        config.headers = Object.assign(this.defaults, config.headers)
        if (config.transformRequest && config.data) {
            config.data = config.transformRequest(config.data, config.headers)
        }

        const chain: Interceptor<any>[] = [
            { onFulfilled: this.dispatchRequest }
        ]
        this.interceptors.request.interceptors.forEach((interceptor: Interceptor<AxiosRequestConfig> | null) => {
            interceptor && chain.unshift(interceptor)
        })
        this.interceptors.response.interceptors.forEach((interceptor: Interceptor<AxiosResponse<T>> | null) => {
            interceptor && chain.push(interceptor);
        })

        let promise: Promise<AxiosRequestConfig> = Promise.resolve(config);
        while (chain.length) {
            const { onFulfilled, onRejected } = chain.shift()!;
            promise = promise.then(onFulfilled, onRejected);
        }

        return promise
    }
    dispatchRequest<T>(config: AxiosRequestConfig): Promise<AxiosResponse<T>> {
        return new Promise<AxiosResponse<T>>(function (resolve, reject) {
            let { method, url, params, headers, data } = config;
            let request = new XMLHttpRequest();
            if (params && typeof params === 'object') {
                params = qs.stringify(params);
            }
            url += (url.indexOf('?') === -1 ? '&' : '?') + params;
            // url! 加个叹号。非空断言
            request.open(method, url, true);
            request.responseType = 'json';
            request.onreadystatechange = function () {
                if (request.readyState === 4 && request.status === 200) {
                    let response: AxiosResponse<T> = {
                        data: request.response ? request.response : request.responseText,
                        status: request.status,
                        statusText: request.statusText,
                        headers: parseHeaders(request.getAllResponseHeaders()),
                        config,
                        request
                    }
                    if(config.transformResponse) {
                        response = config.transformResponse(response)
                    }
                    resolve(response);
                } else {
                    reject('请求失败');
                }
            }
            // if (headers) {
            //     for (let key in headers) {
            //         request.setRequestHeader(key, headers[key]);
            //     }
            // }
            if (headers) {
                for (let key in headers) {
                    if (key === 'common' || allMethods.includes(key)) {
                        if (key === 'common' || key === config.method) {
                            for (let key2 in headers[key]) {
                                request.setRequestHeader(key2, headers[key][key2]);
                            }
                        }
                    }
                }
            }
            let body: string | null = null;
            if (data) {
                body = JSON.stringify(data);
            }
            if(config.cancelToken) {
                config.cancelToken.then((message: string) => {
                    request.abort()
                    reject(message)
                })
            }
            request.send(body);

        });
    }
}